package com.demo.security.access.vote;

import java.util.Collection;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.core.Authentication;

/**
 * 前置安全调用投票器 此类临时使用 绕过 link{org.springframework.security.access.prepost.
 * PreInvocationAuthorizationAdviceVoter} 方法supports(ConfigAttribute
 * attribute)的bug
 * 
 * @author ryuu.kk
 * 
 */
public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter<MethodInvocation> {
	/**日志**/
	protected final Log logger = LogFactory.getLog(getClass());
	/**
	 * 前置通知
	 */
	private final PreInvocationAuthorizationAdvice preAdvice;
	/**
	 * 构造器
	 * @param pre PreInvocationAuthorizationAdvice
	 */
	public PreInvocationAuthorizationAdviceVoter(PreInvocationAuthorizationAdvice pre) {
		this.preAdvice = pre;
	}

	/**
	 * 查找前置权限
	 * @param config 安全配置属性
	 * @return 前置通知
	 */
	
	private PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
		for (ConfigAttribute attribute : config) {
			if (attribute instanceof PreInvocationAttribute) {
				return (PreInvocationAttribute) attribute;
			}
		}
		return null;
	}

	// 增加一个业务层投票方法
	/**
	 * 投票器
	 * @param authentication Authentication
	 * @param method MethodInvocation
	 * @param attributes Collection<ConfigAttribute>
	 * @return 成功标志
	 */
	public int vote(Authentication authentication, MethodInvocation method,	Collection<ConfigAttribute> attributes) {

		PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);

		if (preAttr == null) {
			// No expression based metadata, so abstain
			return ACCESS_ABSTAIN;
		}
		boolean allowed = false;
		try {
			allowed = preAdvice.before(authentication, method, preAttr);
		} catch (RuntimeException ill) {
			// Throwable tb = ill.getCause();
			throw ill;
		}
		return allowed ? ACCESS_GRANTED : ACCESS_DENIED;
	}

	/**
	 * support for security config attribute that spring-security framework.
	 * that resolve PreInvocationAuthorizationAdviceVoter class BUG in support
	 * method.
	 * 
	 * @param attribute security config attribute.
	 * @return support? unsupport
	 */
	public boolean supports(ConfigAttribute attribute) {
		return attribute instanceof PreInvocationAttribute;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return clazz.isAssignableFrom(MethodInvocation.class);
	}

}
